package com.feature50.makeover;

import com.feature50.makeover.filthy.GraphicsUtilities;
import org.jdesktop.animation.timing.Animator;
import org.jdesktop.animation.timing.TimingTargetAdapter;

import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;

public class SlidePanel extends JPanel {
    private JComponent childPanel;

    private Runnable hideFinish;
    private Runnable showFinish;

    private float built = 0f;

    private boolean showing = true;

    private Animator animator;

    private BufferedImage imgChild;
    private BufferedImage imgBackground;

    public SlidePanel(JComponent childPanel, Runnable showFinish, Runnable hideFinish) {
        this.childPanel = childPanel;
        this.showFinish = showFinish;
        this.hideFinish = hideFinish;
        add(childPanel);
        setOpaque(false);
    }

    public BufferedImage getBackgroundImage() {
        return imgBackground;
    }

    public void setBackgroundImage(BufferedImage imgBackground) {
        this.imgBackground = imgBackground;
    }

    public JComponent getChildPanel() {
        return childPanel;
    }

    public void setChildPanel(JComponent childPanel) {
        this.childPanel = childPanel;
    }

    public float getBuilt() {
        return built;
    }

    public void setBuilt(float built) {
        if (built != 1f) {
            setOpaque(false);
        } else {
            setOpaque(childPanel.isOpaque());
        }
        this.built = built;
    }

    @Override
    protected void paintChildren(Graphics g) {
        if (childPanel == null) return;

        if ((built == 0) && (imgBackground == null)) return;

        if (built != 1f) {
            if (!cacheGood()) {
                imgChild = GraphicsUtilities.createCompatibleImage(getWidth(), getHeight());
                Graphics2D g2d = imgChild.createGraphics();
                childPanel.printComponents(g2d);
                g2d.dispose();
            }

            if (showing) {
                // in the process of showing the panel
                int width = getWidth();
                int x = (int) ((float) width * (1f - built)) * -1;

                if (imgBackground != null) g.drawImage(imgBackground, 0, 0, null);
                g.drawImage(imgChild, x, 0, null);
            } else {
                Graphics2D g2d = (Graphics2D) g.create();
                if (imgBackground != null) {
                    g2d.drawImage(imgBackground, 0, 0, null);
                }
                g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, built));
                g2d.drawImage(imgChild, 0, 0, null);
                g2d.dispose();
            }
        } else {
            imgChild = null;
            super.paintChildren(g);
        }
    }

    private boolean cacheGood() {
        if (imgChild == null) return false;
        if (!new Dimension(imgChild.getWidth(), imgChild.getHeight()).equals(getSize())) return false;
        return true;
    }

    @Override
    public void doLayout() {
        if (childPanel == null) return;

        int w = getWidth();
        int h = getHeight();

        if (built == 1f) {
            childPanel.setBounds(0, 0, w, h);
        } else {
            childPanel.setBounds(0 - w, 0, w, h);
        }
    }

    public void hidePanel() {
        startAnimator(false);
    }

    public void showPanel() {
        startAnimator(true);
    }

    private void startAnimator(boolean showPanel) {
        if (animator != null) animator.cancel();

        showing = showPanel;

        animator = new Animator((showing) ? 1000 : 750, new TimingTargetAdapter() {
            @Override
            public void begin() {
                built = (showing) ? 0f : 1f;
                repaint();
            }

            @Override
            public void end() {
                built = (showing) ? 1f : 0f;
                revalidate();
                repaint();

                if (showing) {
                    if (showFinish != null) showFinish.run();
                } else {
                    if (hideFinish != null) hideFinish.run();
                }

                imgBackground = null;
            }

            @Override
            public void timingEvent(float fraction) {
                built = fraction;
                repaint();
            }
        });
        animator.setStartDirection((showing) ? Animator.Direction.FORWARD : Animator.Direction.BACKWARD);
        animator.setStartFraction((showing) ? 0 : 1.0f);
        animator.setDeceleration(0.7f);
        animator.start();
    }
}
